package br.ufpe.cin.camid.remote.distribution.invoker;

import br.ufpe.cin.camid.core.lifecycle.ManagedObjectInvocationException;
import br.ufpe.cin.camid.core.lifecycle.ObjectManagerFacade;
import br.ufpe.cin.camid.core.objects.ApplicationException;
import br.ufpe.cin.camid.core.objects.ManagedObjectHandler;
import br.ufpe.cin.camid.remote.distribution.interceptor.Interceptors;
import br.ufpe.cin.camid.remote.distribution.protocol.RequestMessage;
import br.ufpe.cin.camid.remote.distribution.protocol.ResponseMessage;
import br.ufpe.cin.camid.remote.distribution.protocol.ResponseStatus;

import com.google.inject.Inject;

public class Invoker 
{
	
	private ObjectManagerFacade objectManager;
	
	private Interceptors interceptors;
	
	private InvocationDispatcher dispatcher;
	
	@Inject
	public Invoker(ObjectManagerFacade objectManager, InvocationDispatcher dispatcher)
	{
		this.objectManager = objectManager;
		this.dispatcher = dispatcher;
	}
	
	public ResponseMessage invoke(RequestMessage request)
	{
		ResponseMessage response = new ResponseMessage();
		
		String objectId = request.getObjectId();
		String typeName = request.getDefinitionName();
		ManagedObjectHandler handler = null;
		
		try 
		{
			Class<?> type = Class.forName(typeName);
			handler = objectManager.find(objectId);
			
			if(handler == null)
			{
				handler = objectManager.create(objectId, type);
			}
			
			if(type.getCanonicalName().equals(handler.getManagedObjectType().getCanonicalName()))
			{
				InvocationObject invocationObject = new InvocationObject(request, response, handler);
				
				try 
				{
					handler.acquire();
					//Start of critical session
					dispatcher.doInvoke(invocationObject);
				}
				finally
				{
					//End of critical session
					handler.release();
				}
			}
			else
			{
				Exception ex = new InvocationException(String.format(
					"Type mismatch for object with id %s: expected %s, actual %s", 
					typeName, handler.getManagedObjectType().getCanonicalName()));
				response.setStatus(ResponseStatus.REMOTING_EXCEPTION);
				response.setResponse(ex);
			}
			
		} 
		catch (ClassNotFoundException e) 
		{
			Exception ex = new InvocationException(
				String.format("Type not found for object %s: %s", objectId, typeName));
			response.setStatus(ResponseStatus.REMOTING_EXCEPTION);
			response.setResponse(ex);
		}
		
		
		return response;
	}

	
}
